import java.util.Set;
import java.util.Stack;


public class YVM implements YakaConstants, YVM_Langage {
	
	public static final int VRAI = -1;
	public static final int FAUX = 0;
	
	public static int iterationCompteur = 0;
	public static Stack<Integer> pileIteration;
	public static int conditionCompteur = 0;
	public static Stack<Integer> pileCondition;
	

	private static final String FIN_DE_LIGNE = "\n";
	
	public StringBuffer generation;
	public Stack<String> pileAppelsFonctions;
	
	private Stack<String> pileIdents;
	
	
	public YVM() {
		pileIdents = new Stack<String>();
		pileAppelsFonctions = new Stack<String>();
		pileCondition = new Stack<Integer>();
		pileIteration = new Stack<Integer>();
		resetGeneration();
	}
	
	public void remplirPileIdents()
	{
		Fonction fonction = Yaka.tableIdents.chercheFonction( Yaka.declaration.getProchaineFonction() );
		Set<String> idents = fonction.getIdents().keySet();
		pileIdents.clear();
		/*
		for(String s : idents)
			if (Yaka.tableIdents.getLocaux().get(s).getNature() == Ident.VARIABLE)
				pileIdents.add(s);
		*/
		for(String s : idents)
			if (Yaka.tableIdents.getLocaux().get(s).getNature() == Ident.VARIABLE)
				pileIdents.add(s);
		
		for(String s : idents)
			if (Yaka.tableIdents.getLocaux().get(s).getNature() == Ident.PARAMETRE_FONCTION)
				pileIdents.add(s);
	}
	
	public int getOffsetIdent(String ident) {
		Stack<String> param = Yaka.tableIdents.getParam();
		Stack<String> variables = Yaka.tableIdents.getVariables();
		//System.out.println(ident + " : " + variables.toString());
		if (param.contains(ident))
		{
			for (int i=0; i<param.size();i++)
				if (param.get(i).equals(ident))
					return 2+2*param.size()-2*i;
		}
		else if (variables.contains(ident))
		{
			for (int i=0; i<variables.size();i++)
				if (variables.get(i).equals(ident))
					return -2-2*i;
		}
		return -1;
	}
	
	public Ident getIdent(String ident) {
		return Yaka.tableIdents.chercheIdent(ident);
	}

	public void resetGeneration() {
		generation = new StringBuffer();
	}

	public String getGeneration() {
		return generation.toString();
	}
	
	public void genererLigne(String s) {
		generation.append(s).append(FIN_DE_LIGNE);
		
	}
	
	/*public void genererReservationMemoire(String fonction) {
		ouvreBloc( Yaka.tableIdents.chercheFonction(fonction).getNbVariables() );//pileAppelsFonctions.size() * 2);
	}*/
	
	public void genererCommentaire() {
		generation.append("; ");
	}
	
	
	/* Generation */
	
	public void entete() {
		genererLigne("entete");
	}
	
	public void startupCode() {
		genererLigne("startupCode");
	}
	
	public void etiquetteFonction(String ident){
		genererLigne("	" + ident + ":");
	}
	
	public void fermebloc(int n){
		genererLigne("fermebloc " + n);
	}
	
	public void ireturn(int offset){
		genererLigne("ireturn "+ offset);
	}
	
	public void iconst_ou_iload(String ident_string) {
		Ident ident = getIdent(ident_string);
		
		switch( ident.getNature() ) {
			case Ident.VARIABLE:
				
				iload(ident_string);
				break;
			case Ident.CONSTANTE:
				
				iconst(ident_string);
				break;
			case Ident.PARAMETRE_FONCTION:
				
				iload(ident_string);
				break;
		}
	}
	

	public void reserveRetour(String ident_string){
		Ident ident = getIdent(ident_string);
		
		if (ident.getNature() == Ident.FONCTION)
			genererLigne("reserveRetour");
	}
	
	public void iadd() {
		genererLigne("iadd");
	}
	
	public void isub() {
		genererLigne("isub");
	}	
	
	public void imul() {
		genererLigne("imul");
	}
	
	public void idiv() {
		genererLigne("idiv");
	}
	
	public void inot() {
		genererLigne("inot");
	}
	
	public void ineg() {
		genererLigne("ineg");
	}
	
	public void ior() {
		genererLigne("ior");
	}
	
	public void iand() {
		genererLigne("iand");
	}
	
	public void iinf() {
		genererLigne("iinf");
	}
	
	public void isup() {
		genererLigne("isup");
	}
	
	public void iinfegal() {
		genererLigne("iinfegal");
	}
	
	public void isupegal() {
		genererLigne("isupegal");
	}
	
	public void iegal() {
		genererLigne("iegal");
	}
	
	public void idiff() {
		genererLigne("idiff");
	}
	
	public void iload(String ident) {
		genererLigne("iload " + getOffsetIdent(ident));
	}
	
	public void istore(String ident) {
		genererLigne("istore " + getOffsetIdent(ident));
	}
	
	public void iconst(int entier) {
		genererLigne("iconst " + entier);
	}
	public void iconst2(int entier) {
		genererLigne("iconst " + entier);
	}
	public void iconst(String constante_string) {
		Constante constante = (Constante) getIdent(constante_string); // getConstante ?
		iconst2(constante.getValeur());
	}
	
	public void call(String fonction_string) {
		genererLigne("call " + fonction_string);
	}
	
	public void ifeq(int etiquette) {
		genererLigne("ifeq " + etiquette);
	}
	
	public void goto2(int etiquette) {
		genererLigne("goto " + etiquette);
	}
	
	public void ouvreBloc(int taille) {
		genererLigne("ouvBloc " + taille);
	}
	
	public void aLaLigne() {
		genererLigne("aLaLigne");
	}
	
	public void ecrireChaine(String chaine) {
		genererLigne("ecrireChaine " + chaine);
	}
	
	public void ecrire(){
		if (YakaConstants.TYPE_BOOLEEN == Yaka.expression.getType())
			genererLigne("ecrireBool");
		else
			genererLigne("ecrireEnt");
	}

	public void queue() {
		genererLigne("queue");
	}
	
	public void lireEnt(String ident) {
		genererLigne("lireEnt " + getOffsetIdent(ident));
	}
	
	public void iffauxIteration(int compteur){
		genererLigne("iffaux FAIT" + compteur);
	}
	
	public void gotoCondition(int compteur){
		genererLigne("goto FSI" + compteur);
	}
	
	public void sinon(int compteur){
		genererLigne("SINON" + compteur + " :");
	}
	
	public void fsi(int compteur){
		genererLigne("FSI" + compteur + " :");
	}
	
	public void faire(int compteur){
		genererLigne("FAIRE" + compteur + " :");
	}
	
	public void iffauxCondition(int compteur){
		genererLigne("iffaux SINON" + compteur);
	}
	
	public void gotoIteration(int compteur){
		genererLigne("goto FAIRE" + compteur);
	}
	
	public void fait(int compteur){
		genererLigne("FAIT" + compteur + ":");
	}
}
